package rlex.classes;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;


public class Parser {

	//StringTokenizer
	private List<Token> tokens = new ArrayList<Token>();
	private List<Token> errors = new ArrayList<Token>();	
	private Validator validator = new Validator();
	private StringBuilder program = new StringBuilder();
	private String buffer = "";
	private int index = 0;
	private int lineIndex = 1;
	private int lineNumber = 0;
	private String character = "";
	private String type = "";

	public void readFile(File file) throws IOException {
		BufferedReader input =  new BufferedReader(new FileReader(file));
		String line = null;

		while ((line = input.readLine()) != null){
			lineNumber++;
			program.append(line);
			program.append(System.getProperty("line.separator"));
		}
		input.close();
	}

    public void saveFile(File file, String contents) throws FileNotFoundException {
        if (file == null) {
            throw new IllegalArgumentException("File should not be null.");
        }
        if (!file.isFile()) {
            throw new IllegalArgumentException("Should not be a directory: " + file);
        }
        if (!file.canWrite()) {
          throw new IllegalArgumentException("File cannot be written: " + file);
        }

        //use buffering
        Writer output;
        try {
            output = new BufferedWriter(new FileWriter(file));
          //FileWriter always assumes default encoding is OK!
          output.write(contents);
          output.close();
        } catch (IOException ex) {
            Logger.getLogger(Parser.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

	public String getCharacter() {
		return program.toString().substring(index, index+1);
	}

	public String getNextCharacter() {
		return program.toString().substring(index+1, index+2);
	}

	public void parser() {
		//enquanto nao atingir fim do texto
		while(index != program.length()) {
			//ler caracter
			character = this.getCharacter();
			//se buffer vazio
			if(buffer.equals("")) {
				//verifica o tipo
				//se espaço ou quebra de linha, incrementa e recomeça while
				if(character.equals(" ") || (Pattern.matches("\t", character))) {
					//&& !type.equals(Token.COMMENT)
					index++;
					continue;
				} else if (Pattern.matches("\n", character) || Pattern.matches("\r", character)) {
					lineIndex++;
					index++;
					continue;
				}
				else if (validator.isLetter(character)) {
					type = Token.LETTER;
				}
				else if (validator.isNumber(character)) {
					type = Token.NUMBER;
				} else if (validator.isSpecialCharacter(character)) {
					type = Token.SPECIAL_CHARACTER;
				} else if (validator.isOpenComment(character)) {
					type = Token.COMMENT;
					
				} else {
					System.out.println("***** ERROR - Invalid Character: $"+character+"$, at line "+lineIndex);
					errors.add(new Token("Invalid character", Token.INVALID_CHARACTER, lineIndex));
                    index++;
                    continue;
				}
			} 
			
			if(type.equals(Token.LETTER)) {
				if (validator.isLetter(character) || validator.isNumber(character) || character.equals("_")) {
					buffer += character;
				} else {
					if (validator.isKeyword(buffer.toLowerCase())) {
						tokens.add(new Token(buffer, Token.KEYWORD, lineIndex));
						buffer = "";
						continue;						
					} else if (buffer.equals("or")) {
						tokens.add(new Token(buffer, Token.ADDITIVE, lineIndex));
						buffer="";
						continue;						
					} else if (buffer.equals("and")) {
						tokens.add(new Token(buffer, Token.MULTIPLICATIVE, lineIndex));
						buffer="";
						continue;
					} else if(validator.isBoolean(buffer)) {
						tokens.add(new Token(buffer, Token.BOOLEAN, lineIndex));
						buffer="";
						continue;
					} else if (validator.isIdentifier(buffer)) {
						tokens.add(new Token(buffer, Token.IDENTIFIER, lineIndex));
						buffer="";
						continue;
					} else {
                        errors.add(new Token(buffer, Token.INVALID_CHARACTER, lineIndex));
                        buffer="";
                        continue;
                    }
				}
			} else if (type.equals(Token.NUMBER)) {
				if (validator.isNumber(character) || character.equals(".")) {
					buffer += character;					
				} else {
					if (validator.isInteger(buffer)) {
						tokens.add(new Token(buffer, Token.INTEGER, lineIndex));
						buffer="";
						continue;
					} else if (validator.isReal(buffer)) {
						tokens.add(new Token(buffer, Token.REAL, lineIndex));
						buffer="";
						continue;
					}
				}
			} else if (type.equals(Token.SPECIAL_CHARACTER)) {
                if (validator.isMultiplicativeOperator(character)) {
					tokens.add(new Token(character, Token.MULTIPLICATIVE, lineIndex));
					index++;
					continue;
				} else if (validator.isAddOperator(character)) {
					tokens.add(new Token(character, Token.ADDITIVE, lineIndex));
					index++;
					continue;
				} else if(validator.isDelimiter(character)) { 
					if((character.equals(Token.DOUBLENET)) && (this.getNextCharacter().equals("="))) {
							index++;
							tokens.add(new Token(":=", Token.ALLOCATION, lineIndex));							
					} else {
						tokens.add(new Token(character, Token.DELIMITER, lineIndex));
					}
				} else if (validator.isRelationalOperator(character)) {
					if ((character.equals("<")) && (getNextCharacter().equals(">"))) {
						character = "<>";
					} else if ((character.equals("<")) && (getNextCharacter().equals("="))) {
						character = "<=";
					} else if ((character.equals(">")) && (getNextCharacter().equals("="))) {
						character = ">=";
					}
					tokens.add(new Token(character, Token.RELATIONAL, lineIndex));
				}
			} else if (type.equals(Token.COMMENT)) {
					buffer+=character;
					if (validator.isCloseComment(character)) {
						//tokens.add(new Token(buffer, type, 0));
						buffer="";
					} 
			}
			index++;
        }
        System.out.print("Buffer: "+buffer);
		if (buffer != null && !buffer.equals("")) {
			errors.add(new Token("Comment not closed", Token.COMMENT, lineIndex));
		}
		
	}

	public void listTree() {
		for (Token token : tokens) {
			System.out.println("tree: "+token.getName()+" - order: "+token.getLine());				
		}
	}
	
	public void listToken() {
		for (Token token : tokens) {
			System.out.println("token: "+token.getName()+" - "+token.getType()+" - "+token.getLine());				
		}
	}
	
	public void listErrors() {
		for (Token token : errors) {
			System.out.println("error: "+token.getName()+" - "+token.getType()+" - "+token.getLine());				
		}
	}

    public List<Token> getErrors() {
        return errors;
    }

    public void setErrors(List<Token> errors) {
        this.errors = errors;
    }

    public List<Token> getTokens() {
        return tokens;
    }

    public void setTokens(List<Token> tokens) {
        this.tokens = tokens;
    }

}
